package cate.game.role.pvp.pointrace;

import cate.common.table.d.*;
import cate.common.table.pvp.pointrace.row.PointRaceRewardSeasonRow;
import cate.common.table.role.vip.RoleVipRow;
import cate.game.pvp.pointrace.data.PointRaceKV;
import cate.game.pvp.pointrace.data.PointRaceOpponent;
import cate.game.pvp.pointrace.handler.PointRaceHandler;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.role.Role;
import cate.game.role.farm.AbstractFarm;
import cate.game.role.fight.FuncPosition;
import cate.game.role.fight.Position;
import cate.game.role.pvp.PvpRecord;
import cate.game.role.pvp.pointrace.msg.PointRaceBuyTicketResp;
import cate.game.role.pvp.pointrace.msg.PointRaceDataResp;
import cate.game.role.pvp.pointrace.msg.PointRaceLiteDataResp;
import cate.game.role.pvp.pointrace.msg.PointRaceSeasonRewardResp;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.springframework.data.annotation.Transient;

import java.util.*;

import static cate.common.table.d.GDFunc.POINT_RACE;
import static cate.common.table.d.GDFunc.POINT_RACE_DEF;
import static cate.common.table.d.GDPointRace.POINTS_MIN;
import static cate.common.table.d.GDPointRace.WHOLE_SEASON_MILLIS;
import static cate.game.client.msg.MsgSender.send;

@NoteClass(value = "积分赛")
public class PointRace extends AbstractFarm {
    @NoteField(value = "当前赛季")
    public int season;

    @NoteField(value = "当前赛季开启时间")
    public long seasonStartTime;

    @Transient
    @NoteField(value = "当前赛季持续时间")
    public long wholeSeasonTime = WHOLE_SEASON_MILLIS;

    @NoteField(value = "积分")
    public long points;

    @NoteField(value = "赛季宝箱领取记录", detail = "<宝箱TID, 是否领取>")
    public Map<Integer, Boolean> rewardGot;

    @NoteField(value = "今日挑战次数")
    public int challengeTimes;

    @NoteField(value = "已购买的挑战次数")
    public int buyChallengeTimes;

    @NoteField(value = "比赛记录")
    public LinkedList<PvpRecord> recordList;

    @NoteField(value = "本赛季挑战次数")
    public int seasonChallengeTimes;

    @NoteField(value = "上一次找对手的时间")
    public long lastFindOpponentsTime;

    @NoteField(value = "自己的对手列表")
    public List<PointRaceOpponent> opponents;

    @NoteField(value = "最近一次主动挑战的记录，因为提前结算积分了，给战斗结束时的处理使用")
    @JsonIgnore
    @Transient
    public transient PvpRecord lastInitiativeRecord;

    @JsonProperty("rank")
    public int getRank() {
        return role.getGame().pvp.pointRace.getHandler().findRank(role);
    }

    @JsonProperty("seasonStartTime")
    @SuppressWarnings("unused")
    public long getSeasonStartTime() {
        return role.getGame().pvp.pointRace.getHandler().getSeasonStartTime();
    }

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (rewardGot == null) {
            rewardGot = new HashMap<>();
        }
        if (recordList == null) {
            recordList = new LinkedList<>();
        }
        if (opponents == null) {
            opponents = new ArrayList<>();
        }
        if (points == 0) {
            points = 1000;
        }

        if (lastFindOpponentsTime > System.currentTimeMillis()) {
            lastFindOpponentsTime = System.currentTimeMillis();
        }

        this.wholeSeasonTime = WHOLE_SEASON_MILLIS;
    }

    public void checkSeason() {
        if (isOpen()) {
            int serverSeason = role.getGame().pvp.pointRace.getHandler().getSeason();
            seasonStartTime = role.getGame().pvp.pointRace.getHandler().getSeasonStartTime();
            if (season == 0 && serverSeason > 0) {
                points = POINTS_MIN;
                clearData();
                season = serverSeason;
                seasonStartTime = role.getGame().pvp.pointRace.getHandler().getSeasonStartTime();
                return;
            }
            if (season < serverSeason) {
                clearData();
                season = serverSeason;
                seasonStartTime = role.getGame().pvp.pointRace.getHandler().getSeasonStartTime();
            }
        }
    }

    @Override
    public void onEnter() {
        checkSeason();
        checkDailyRankTask();
    }

    private void checkDailyRankTask() {
        PointRaceKV kv = role.getGame().pvp.pointRace.getDataManager().getData();
        for (int i = 1; i <= 7; i++) {
            Map<String, Integer> dailyRankRd = kv.getDailyRankRd().get(i);
            if (!Objects.isNull(dailyRankRd)) {
                Integer rank = dailyRankRd.remove(role.getUid());
                if (Objects.nonNull(rank)) {
                    role.getHistory().action.addPointRaceRank(rank);
                    role.getHistory().action.pointRaceRankTimes(rank);
                }
            }
        }
    }

    public void clearData() {
        opponents.clear();
        rewardGot.clear();
        recordList.clear();
        challengeTimes = 0;
        buyChallengeTimes = 0;
        seasonChallengeTimes = 0;
        lastFindOpponentsTime = 0;
        lastInitiativeRecord = null;
        role.getPvp().pointRaceMark.clearData();
    }

    public void refreshOpponents(List<PointRaceOpponent> list) {
        opponents = list;
        lastFindOpponentsTime = System.currentTimeMillis();
    }

    @Override
    public int getFuncId() {
        return POINT_RACE;
    }

    @Override
    public void onAfterOpen() {
        noticeUpdate();
    }

    @Override
    public int getFightFuncId() {
        return POINT_RACE;
    }

    public EcResult<Void> challengeCheck(String opponentUid, boolean skip) {
        EcResult<Void> r = new EcResult<>();
        if (fighting) {
            return r.fail("当前战斗未结束");
        }
        if (skip && !canSkip()) {
            return r.fail(String.format("%d级前需每赛季挑战%d场才可跳过", GDPointRace.SKIP_CHALLENGE_LEVEL, GDPointRace.SKIP_CHALLENGE_TIMES));
        }

        if (challengeTimes >= GDPointRace.FREE_CHALLENGE_TIMES) {
            MixRes reward = new MixRes(GDObj.getMixResStr(GDObj.Type.PROP, GDPointRace.TICKET_TID, 1));
            if (!reward.consumeCheck(role, null).ok()) {
                return r.fail("挑战券不足");
            }
        }
        if (opponents == null || opponents.stream().noneMatch(e -> e.getSnapshot().uid.equals(opponentUid))) {
            return r.fail("选择了错误的对手");
        }
        Position adventurePosition = role.getFight().getPosition(GDFunc.ADVENTURE);
        if (adventurePosition == null || adventurePosition.grids.isEmpty()) {
            return r.fail("请设置竞技场进攻阵容!");
        }
        Position defPosition = role.getFight().getPosition(GDFunc.POINT_RACE_DEF);
        if (defPosition == null || defPosition.grids.isEmpty()) {
            FuncPosition funcPosition = new FuncPosition();
            funcPosition.init();
            funcPosition.funcId = POINT_RACE_DEF;
            funcPosition.positions.add(adventurePosition);
            role.getGame().fight.position.updatePosition(role, funcPosition);
        }
        return r;
    }

    public boolean canSkip() {
        return role.getBase().level >= GDPointRace.SKIP_CHALLENGE_LEVEL
                || seasonChallengeTimes >= GDPointRace.SKIP_CHALLENGE_TIMES;
    }

    public void challenge() {
        if (challengeTimes >= GDPointRace.FREE_CHALLENGE_TIMES) {
            MixRes reward = new MixRes(GDObj.getMixResStr(GDObj.Type.PROP, GDPointRace.TICKET_TID, 1));
            reward.consumeDirect(role, null);
        }
        challengeTimes++;
        seasonChallengeTimes++;
    }

    /**
     * 购买挑战券
     *
     * @param num 购买的数量
     */
    public EcResult<Void> buyTicket(int num) {
        EcResult<Void> r = new EcResult<>();
        if (num <= 0) {
            return r.fail("购买数量有误");
        }
        RoleVipRow vipRow = role.getGame().table.vip.base.getByLevel(role.getBase().vip);
        if (num + buyChallengeTimes > vipRow.ticketNum) {
            return r.fail("购买次数不足");
        }
        MixRes consume = new MixRes(GDPointRace.TICKET_CONSUME_STR).multiply(num);
        EcResult<MixResItem> consumeRes = consume.consume(role, null);
        if (!consumeRes.ok()) {
            return r.fail(consumeRes.message);
        }
        buyChallengeTimes += num;
        MixRes reward = new MixRes();
        reward.addItem(new MixResItem(GDObj.Type.PROP, GDPointRace.TICKET_TID, num));
        reward.add(role, true, 0);
        role.getGame().notice.dialogReward(role, reward);
        role.sendNow(new PointRaceBuyTicketResp(buyChallengeTimes));
        return r.success();
    }

    /**
     * 领取每周宝箱
     *
     * @param tid 宝箱的ID
     */
    public EcResult<Void> getSeasonReward(int tid) {
        EcResult<Void> r = new EcResult<>();
        Boolean got = rewardGot.get(tid);
        if (got != null) {
            return r.fail("奖励已领取");
        }
        PointRaceRewardSeasonRow row = role.getGame().table.pointRace.rewardSeason.get(tid);
        if (row == null) {
            return r.fail("奖励不存在");
        }
        if (seasonChallengeTimes < row.times) {
            return r.fail("挑战次数不足");
        }
        MixRes reward = new MixRes(row.rewardStr);
        reward.add(role, true, GDOperation.POINT_RACE_SEASON_REWARD);
        rewardGot.put(tid, true);
        role.sendNow(new PointRaceSeasonRewardResp(rewardGot));
        role.getGame().notice.dialogReward(role, reward);

        if (role.getGame().table.pointRace.rewardSeason.get(tid + 1) == null) {
            role.getHistory().action.pointRaceFinalReward();
        }
        return r.success();
    }

    /**
     * 加入比赛记录
     */
    public synchronized void addRecord(PvpRecord record, boolean initiative) {
        if (recordList.size() >= 20) {
            recordList.removeFirst();
        }
        recordList.add(record);
        if (initiative) {
            lastInitiativeRecord = record;
        }
    }

    @Override
    public void onDaySpan(boolean silence) {
        challengeTimes = 0;
        buyChallengeTimes = 0;
        if (!silence) {
            role.sendNow(new PointRaceDataResp(this));
        }
    }

    public void noticeUpdate() {
        if (isOpen()) {
            checkSeason();
            role.getGame().pvp.pointRace.getHandler().syncRolePoints(role);
            role.getGame().pvp.pointRace.getHandler().findOpponents(role);
            role.sendNow(new PointRaceDataResp(this));
        }
    }

    public void liteNotice() {
        PointRaceHandler handler = role.getGame().pvp.pointRace.getHandler();
        int serverSeason = handler.getSeason();
        send(role::sendNow, new PointRaceLiteDataResp(
                handler.getSeasonStartTime(),
                serverSeason != season ? 0 : challengeTimes,
                serverSeason != season ? 0 : seasonChallengeTimes,
                serverSeason != season ? null : rewardGot,
                handler.findRank(role)
        ));
    }
}
